home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume13 / vn.jan.88 / part02 < prev    next >
Encoding:
Internet Message Format  |  1988-01-30  |  41.6 KB

  1. Subject:  v13i020:  VN newsreader, 1/88 version, Part02/05
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rsalz@uunet.UU.NET
  5.  
  6. Submitted-by: Bob Mcqueer <amdahl!rtech!rtech!bobm@UUNET.UU.NET>
  7. Posting-number: Volume 13, Issue 20
  8. Archive-name: vn.jan.88/part02
  9.  
  10. #! /bin/sh
  11. # This is a shell archive, meaning:
  12. # 1. Remove everything above the #! /bin/sh line.
  13. # 2. Save the resulting text in a file.
  14. # 3. Execute the file with /bin/sh (not csh) to create the files:
  15. #    server.doc
  16. #    std.c
  17. #    std.h
  18. #    config_std.h
  19. export PATH; PATH=/bin:$PATH
  20. echo shar: extracting "'server.doc'" '(13304 characters)'
  21. if test -f 'server.doc'
  22. then
  23.     echo shar: will not over-write existing file "'server.doc'"
  24. else
  25. cat << \SHAR_EOF > 'server.doc'
  26. vns_envir()
  27.  
  28.     called to allow server layer to set environment.  Will be called
  29.     before anything else.  See the vn_env() routine if the server
  30.     interface intends to use any environment variables.
  31.  
  32.     There are some procedure pointers in vn which may optionally
  33.     be set by the server interface.  By default, all of these are
  34.     NULL:
  35.  
  36.     (*Massage)(hdr)
  37.     ARTHEADER *hdr;
  38.  
  39.         Mail path massaging function.  Allows server interface to
  40.         interact with the user prior to editing the reply file
  41.         on mail replies.  See vns_aopen().
  42.  
  43.     (*Headedit)()
  44.  
  45.         If set, will be called instead of the default action when
  46.         the user chooses the "toggle header flag" command.  This
  47.         is intended to allow the server interface to implement a
  48.         user selection of which headers to display when reading
  49.         an article.  If this is set, note that the reader code will
  50.         never rewind an article past the point set by vns_aopen().
  51.  
  52.     (*Postfunc)(hdr,fn)
  53.     (*Mailfunc)(hdr,fn)
  54.     ARTHEADER *hdr;
  55.     char *fn;
  56.  
  57.         If set, these functions will be called for mail / post
  58.         instead of spawning a command.  They are intended for
  59.         the instance in which these operations can be handled
  60.         within the executable rather than forking another process.
  61.         If forking another process has to be done, you might as
  62.         well set up postcmd / mailcmd, and let vn do it.  fn is
  63.         the name of a file containing the user's article or mail
  64.         reply.
  65.  
  66.     All of the procedures will be called with the user's terminal in
  67.     raw mode and will be expected to leave it that way on return.
  68.     If these routines need to control ioctl settings or send control
  69.     sequences to the terminal, they should do so through the tty_set /
  70.     term_set calls used by the rest of vn.  These routines basically
  71.     hide ioctl / termcap from everybody.
  72.  
  73. vns_news (argc,argv,lfirst,unsub)
  74. int argc;
  75. char **argv;
  76. int *lfirst;
  77. int *unsub;
  78.  
  79.     called to let server layer process options and enter known newsgroups
  80.     in vn structure.  arguments are vn's command line.   It calls four
  81.     specific support routines:
  82.  
  83.     hashenter(group,highnum,lownum)
  84.     char *group;
  85.     int highnum, lownum;
  86.  
  87.         Enter information about a newsgroup. Group is newsgroup
  88.         name, highnum the highest article number, lownum the lowest.
  89.  
  90.     NODE *
  91.     hashfind(group)
  92.     char *group;
  93.  
  94.         Return information on entered group.  See node.h header
  95.         file for structure and legal usage of its items by server
  96.         interface routines.  May be called by other vns_ rouines
  97.         as well.
  98.  
  99.     fw_group(group,new,sub,read,look)
  100.     char *group;
  101.     int new, sub, read, look;
  102.  
  103.         prepare to enter display information for a new newsgroup.
  104.         new is non-zero if this is to be considered a "new" newsgroup
  105.         since the user's last session.  sub is non-zero if user is
  106.         to be considered "subscribed" to it.  read is the initial
  107.         "articles read" number for the newsgroup.  look is non-zero
  108.         if server interface intends to look for articles in this
  109.         newsgroup.  This parameter is open to interpretation, but
  110.         should indicate that user's options mean group is to be looked
  111.         at.  This should NOT reflect the fact that no articles will
  112.         be found because "read" is up to date.  The parameter is only
  113.         used for statistical collection, anyway.
  114.  
  115.     fw_art(anum,subj,lines,author)
  116.     int anum;
  117.     char *subj, *lines, *author;
  118.  
  119.         enter information for an article in the newsgroup last set
  120.         with fw_group().  anum's should be entered in ascending
  121.         sequence.
  122.  
  123.     fw_chg(new,sub,read,look)
  124.     int new, sub, read, look;
  125.  
  126.         change the parameters for the current group.  To allow the
  127.         server interface to modify its setting for the group based
  128.         on what it discovered while scanning the articles.
  129.  
  130.     fw_group() should be called once and only once for each group
  131.     hashenter()'ed.  The order of fw_group() calls determines the
  132.     order of vn's display to the user.  fw_group() calls may be made
  133.     after all the hashenter() calls have been made, or they may be
  134.     interleaved.  If no fw_art() calls are made following a particular
  135.     fw_group() call, no display page will be generated for that group.
  136.     If vns_news does not stick to the rules regarding calls to fw_group,
  137.     vn will exit with a fatal error message.
  138.  
  139.     Rationale for this is for the server layer to have no knowledge
  140.     of device dependencies (the calls to fw_art will split up display
  141.     pages), or any of the vn structures beyond the newsgroup information.
  142.     And the newsgroup NODE structure has clearly documented pieces
  143.     which are legal for the server to read or use, and others which are
  144.     out-of-bounds.  It isolates things fairly well.
  145.  
  146.     Note that vns_news hides all knowledge of the user's .newsrc file or
  147.     similar concept, and all knowledge of where articles live and how
  148.     they are administered.  Error handling is brutally simple.  If the
  149.     server doesn't obey the rules, vn will exit with a fatal error message
  150.     if it detects the fact.
  151.  
  152.     lfirst and unsub are returned to the caller based on the command
  153.     line:
  154.  
  155.         lfirst - non-zero if vn is supposed to go directly to
  156.             the newsgroup summary list on startup (-% option).
  157.  
  158.         unsub - set non-zero if unsubscribed newsgroups are to
  159.             be updated on "write alls" (-U option).
  160.  
  161.     We COULD have vn look for these, and nail them down, but most command
  162.     line options are things which make sense only to the server interface.
  163.     Different environments may have different options which make sense.
  164.     Might as well give the server interface control of the command line
  165.     exclusively, and advise to consistently use -% and -U if possible.
  166.  
  167.     See below for some other support routines which might be useful for
  168.     vns_news().
  169.  
  170.     In summary, what vns_news should do:
  171.  
  172.         Call hashenter() for all the legitimate newsgroups.
  173.         After this, hashfind() may be called at any time to retrieve
  174.         the information about a group.
  175.  
  176.         Call fw_group() once and only once for each group, in the
  177.         order newsgroups should be displayed to the user.  The order
  178.         need bear no relation to the order of hashenter calls.
  179.  
  180.         After each fw_group call, shovel in article information with
  181.         fw_art calls.  Optionally call fw_chg() if anything ought to
  182.         be changed from the original fw_group call.
  183.  
  184.         If there are fatal errors call printex() with an explanation.
  185.  
  186.         To produce non-fatal diagnostics / informational messages,
  187.         call fgprintf().  If you absolutely want to produce the message,
  188.         backgrounded or not, you CAN simply output to stderr / stdout,
  189.         but think before doing it.  It's obnoxious to have a
  190.         backgrounded program stop on output before it's ready to
  191.         interact with you.
  192.  
  193.         ISN'T THAT EASY?
  194.  
  195. FILE *vns_aopen(art,hdr)
  196. int art;
  197. ARTHEADER *hdr;
  198.  
  199.     Returns an open file pointer to article text for vn's use, or
  200.     NULL for failure.  Fills in the ARTHEADER argument, containing
  201.     information conceptually thought of as extracted from the "header"
  202.     lines.  File pointer should be positioned at the start of the
  203.     bonafide article text, if headers are contained in the file.  See
  204.     head.h for the items to be filled in.
  205.  
  206.     If (*Massage)(), (*Postfunc)() or (*Mailfunc)() are defined, they
  207.     will be called with this same ARTHEADER instance when they are
  208.     called.  vns_aopen may set up any arguments in the priv item
  209.     for the convenience of these routines.
  210.  
  211.     CAUTION:
  212.         hdr contains string pointers.  vns_aopen must be sure to
  213.         point to legitimate storage, not its stack.  And it SHOULD
  214.         NOT simply malloc() the strings every time without freeing
  215.         the old ones somehow - this will lose storage every time the
  216.         user reads a new article.  You MAY NOT assume that the same
  217.         instance of an ARTHEADER structure is passed to each
  218.         vns_aopen() call, either.  See str_tpool, and associated
  219.         routines, below.
  220.  
  221. vns_close(fp)
  222. FILE *fp;
  223.  
  224.     Called to close article opened with vns_open().  In many instances
  225.     will simply be an fclose().  vn will NEVER have multiple articles
  226.     open, so vns_aopen / vns_aclose may maintain static information if
  227.     they like.
  228.  
  229. vns_gset(grp)
  230. char *grp;
  231.  
  232.     Called as the user moves from page to page of the display.  vns_aopen
  233.     and vns_aclose calls will refer to this newsgroup.  May do chdir()'s
  234.     if it likes.
  235.  
  236. vns_exit(stat)
  237. int stat;
  238.  
  239.     Called before exit to allow the server layer to clean up.  Called
  240.     on normal exit & from within printex() (ie. DON'T call printex in
  241.     unconditionally in this routine)
  242.  
  243. vns_write(news,count)
  244. NODE **news;
  245. int count;
  246.  
  247.     Called to do whatever is the conceptual equivalent of updating the
  248.     user's .newsrc file.  news is an array of newsgroup nodes in the
  249.     order that fw_group was called, count is the length of the array.
  250.     Vn may have modified the rdnum items & the FLG_SUB flag based on
  251.     user interaction.  User exit, "write" commands, and "subscription"
  252.     commands will result in this call.  vn will set a flag bit, FLG_ECHG,
  253.     in the flag item of all newsgroups whose data it changed since last
  254.     call.  This bit may be used by vns_write to do selective updates, and
  255.     will be cleared for all groups following return from vns_write().
  256.  
  257. vns_asave(art,fp,count,name,mode)
  258. int art;
  259. FILE *fp;
  260. int count;
  261. char *name,*mode;
  262.  
  263.     Called to shovel an article out to an open file pointer.  Intended
  264.     to allow the server interface to implement any kind of massaging
  265.     of the article text that is called for.  count starts with 0, and
  266.     counts the articles being written on this opening of the file.
  267.     name is the file name, and mode is the mode it was opened with.
  268.     This information is being passed in to allow implementation of
  269.     selective copy of header lines, etc. based on the state of the
  270.     file.  Note that it WILL exist since it's already been opened.  On
  271.     count 0, this routine may be interested in whether it's empty or not.
  272.     This routine WILL get called between vns_aopen and vns_aclose.
  273.  
  274. char *Vns_version;
  275.  
  276.     The server layer must define this string.  It should be a
  277.     short string which describes the server type / version (something
  278.     like "NNTP-1.0").  Will be printed as part of the vn version message.
  279.  
  280. Some support routines:
  281.  
  282.     regex(), regcmp(), strtok() and strpbrk() are standard SYSV
  283.     routines not normally available for BSD.  vn uses them, so
  284.     they are implemented in vn for those systems.  Thus, they are
  285.     available for use in the server interface.  Be careful with
  286.     strtok(), which retains context from call to call, if you are
  287.     not familiar with its use.  To free storage handed you by
  288.     regcmp(), use regfree() instead of free().
  289.  
  290.     char *vn_env(var, def)
  291.     char *var, *def;
  292.  
  293.     Use this in place of getenv() to be consistent with the rest of
  294.     vn.  This routine checks first for the variable prefixed with "VN"
  295.     if it doesn't already begin with "VN".  If neither the prefixed
  296.     or unprefixed name exists, it returns the def argument.  This allows
  297.     consistent override of EDITOR with VNEDITOR, etc.
  298.  
  299.     printex(....)
  300.  
  301.     printf style argument list.  Generates a fatal error and exits.
  302.     No newline necessary in the message, and printex() adds the error
  303.     number to its output automatically.  printex() may be called from
  304.     anywhere in the server interface to generate a fatal error, except
  305.     from vns_exit().
  306.  
  307.     fgprintf(....)
  308.  
  309.     nonfatal diagnostic / informational message.  printf style argument
  310.     list.  No extra formatting printed.  Will only print if program is
  311.     running in the foreground.  fgprintf() should be called only from
  312.     vns_envir() and vns_news(), as all the other routines come after
  313.     user interaction has begun.
  314.  
  315.     char *
  316.     str_store(s)
  317.     char *s;
  318.  
  319.     Returns a pointer to a permanent copy of a string.  DO NOT FREE THAT
  320.     POINTER!!  This routine is intended to save malloc() overhead, and
  321.     returns pointers into larger buffer allocated with a single request.
  322.  
  323.     char *
  324.     str_tstore(ptr,s)
  325.     char *ptr;
  326.     char *s;
  327.  
  328.     char *
  329.     str_tpool(n)
  330.     int n;
  331.  
  332.     char **
  333.     str_taptr(ptr)
  334.     char *ptr;
  335.  
  336.     str_tfree(ptr)
  337.     char *ptr;
  338.  
  339.     Temporary string storage.  str_tpool is called with the maximum
  340.     number of strings to be allocated, and it returns a pointer to use
  341.     with the other calls (ptr argument).  str_tstore returns allocated
  342.     copies of strings.  str_taptr returns an address which may be treated
  343.     as a string array containing the str_tstore allocated strings
  344.     beginning with the NEXT str_tstore() call.  May be called several
  345.     times to get several array pointers.  str_tfree() frees all the strings
  346.     allocated on the given ptr returned from str_tpool, as well as the
  347.     pool pointer itself.  The returned addresses are no longer valid, nor
  348.     is the pool pointer.  Multiple pool pointers may be maintained.
  349.     Pool pointer contents should not be touched by the caller.  These
  350.     routines are intended for the convenience of vns_aopen().
  351.  
  352. HEADER FILES:
  353.  
  354. server.h includes the header files used by vn which are intended to be known
  355. to server interface routines.  The server interface routines may include
  356. system headers, this one, and their own headers.  Among their own headers
  357. should be a config_*.h containing parameters which are likely to be changed
  358. from installation to installation, such as file paths.  server.h contains
  359. node.h and head.h, defining the NODE and ARTHEADER structures.
  360.  
  361. A useful constant defined in server.h is RECLEN.  This should be used only
  362. as an array dimension for a stack character buffer, intended to be large
  363. enough to hold an arbitrary string.  If you use this constant, you are
  364. making "large enough" the same value used in vn.  Use it for fgets()
  365. buffers, or sprintf buffers prior to making allocated copies of the string,
  366. and you'll be working about the way vn does in a lot of places.
  367. SHAR_EOF
  368. fi # end of overwriting check
  369. echo shar: extracting "'std.c'" '(24192 characters)'
  370. if test -f 'std.c'
  371. then
  372.     echo shar: will not over-write existing file "'std.c'"
  373. else
  374. cat << \SHAR_EOF > 'std.c'
  375. #include <stdio.h>
  376. #include <pwd.h>
  377. #include <ctype.h>
  378. #include <sys/param.h>
  379. #include "server.h"
  380. #include "config_std.h"
  381. #include "std.h"
  382.  
  383. #ifndef MAXPATHLEN
  384. #define MAXPATHLEN 240
  385. #endif
  386.  
  387. extern NODE *hashfind();
  388. extern FILE *fopen();
  389. extern char *index(), *rindex();
  390. extern char *malloc();
  391. extern char *str_tstore(), *str_tpool(), *str_store();
  392. extern char *strtok(), *strpbrk();
  393. extern char *regex(), *regcmp();
  394.  
  395. #ifdef MAILCHOOSE
  396. extern int (*Massage)();
  397. #endif
  398.  
  399. /*
  400.     global flags signifying options set
  401. */
  402. #define GF_ALL 1    /* -x option - scan everything */
  403. #define GF_SPEC 2    /* -n option(s) - user specified groups */
  404. #define GF_OVER 4    /* command line specification - overide marks */
  405.  
  406. char *Vns_version = "res1.1";
  407.  
  408. static char *Onews, *Newsrc;
  409. static int Ntopt, Nntopt, Nwopt, Nnwopt;
  410.  
  411. static char *Wopt[NUMFILTER];        /* regular expressions for -w options */
  412. static char *Topt[NUMFILTER];        /* for -t options */
  413. static char *Negwopt[NUMFILTER];    /* for negated -w options */
  414. static char *Negtopt[NUMFILTER];    /* for negated -t options */
  415.  
  416. static char *Options[OPTLINES];
  417. static int Max_name, Optlines;
  418. static unsigned Gflags = 0;
  419. static char **Active;
  420. static int Actnum;
  421. static char *Mailer, *Poster;
  422.  
  423. static char *RT_head = RTHEAD;
  424. static char *P_head = PHEAD;
  425. static char *M_head = MHEAD;
  426. static char *R_head = RHEAD;
  427. static char *TO_head = TOHEAD;
  428. static char *F_head = FHEAD;
  429. static char *FT_head = FTHEAD;
  430. static char *T_head = THEAD;
  431. static char *DIS_head = DISHEAD;
  432. static char *L_head = LHEAD;
  433. static char *N_head = NHEAD;
  434.  
  435. static char *Fpfix = FPFIX;
  436.  
  437. /*
  438. **    environment setup.
  439. */
  440. vns_envir()
  441. {
  442.      char dbuf[MAXPATHLEN], *rcname;
  443.     char *vn_env();
  444.     struct passwd *ptr, *getpwuid();
  445. #ifdef MAILCHOOSE
  446.     int mail_prompt();
  447.  
  448.     Massage = mail_prompt;
  449. #endif
  450.  
  451.     ptr = getpwuid (getuid());
  452.  
  453.     rcname = vn_env("MAILER",DEF_MAIL);
  454. #ifdef INLETTER
  455.     sprintf(dbuf,"cat %%s | %s",rcname);
  456. #else
  457.     /* used as a format string TWICE (%%%% -> %% -> %) */
  458.     sprintf(dbuf,"cat %%%%s | %s %%s",rcname);
  459. #endif
  460.     Mailer = str_store(dbuf);
  461.     rcname = vn_env("VNPOSTER",DEF_POST);
  462.     sprintf(dbuf,"%s %%s",rcname);
  463.     Poster = str_store(dbuf);
  464.     rcname = vn_env("NEWSRC",DEF_NEWSRC);
  465.     if (*rcname != '/')
  466.     {
  467.         sprintf (dbuf, "%s/%s",ptr->pw_dir,rcname);
  468.         Newsrc = str_store (dbuf);
  469.     }
  470.     else
  471.         Newsrc = str_store (rcname);
  472.  
  473.     /* above logic guarantees that Newsrc contains a '/' */
  474.      strcpy(dbuf,Newsrc);
  475.      strcpy(rindex(dbuf,'/')+1,".vnXXXXXX");
  476.     mktemp(dbuf);
  477.     Onews = str_store (dbuf);
  478.  
  479.     if (access (Newsrc,0) != 0)
  480.         creat (Newsrc,0666);
  481. }
  482.  
  483. /*
  484.     change directory to group
  485. */
  486. vns_gset(grp)
  487. char *grp;
  488. {
  489.     char dbuf [RECLEN];
  490.     g_dir (grp,dbuf);
  491.     if (chdir(dbuf) < 0)
  492.         printex("can't change to newsgroup directory");
  493. }
  494.  
  495. /*
  496.     g_dir converts newsgroup name to directory string
  497. */
  498. static
  499. g_dir(s,t)
  500. char *s,*t;
  501. {
  502.     char *ptr;
  503.     sprintf (t,"%s/%s",SPOOLDIR,s);
  504.     for (ptr=t+strlen(SPOOLDIR)+1; (ptr = index(ptr,'.')) != NULL; *ptr = '/')
  505.         ;
  506. }
  507.  
  508. /*
  509. ** myfind is used for hashfind() calls which aren't supposed to fail.
  510. */
  511. static NODE *
  512. myfind(name)
  513. char *name;
  514. {
  515.     NODE *n;
  516.  
  517.     n = hashfind(name);
  518.     if (n == NULL)
  519.         printex("Unexpected table lookup failure");
  520.     return (n);
  521. }
  522.  
  523. vns_news(argc,argv,lfirst,nun)
  524. int argc;
  525. char **argv;
  526. int *lfirst, *nun;
  527. {
  528.     FILE *fp;
  529.     static char marks[] =
  530.     { 
  531.         NEWS_ON, NEWS_OFF, '\0' 
  532.     };
  533.     int line, len, num;
  534.     char buf [RECLEN], trail, optpflag, submark, *fret, *ptr;
  535.  
  536.     ++argv;
  537.     --argc;
  538.  
  539.     /* fill table with active newsgroups */
  540.     fill_active ();
  541.  
  542.     if (argc > 0)
  543.     {
  544.         Gflags |= GF_OVER;
  545.         arg_opt(argc,argv,lfirst,nun);
  546.         optpflag = 'y';
  547.     }
  548.     else
  549.         optpflag = 'n';
  550.  
  551.     if ((fp = fopen (Newsrc,"r")) == NULL)
  552.         printex ("can't open %s for reading",Newsrc);
  553.  
  554.     Optlines = 0;
  555.  
  556.     for (line = 1; (fret = fgets(buf,RECLEN-1,fp)) != NULL && emptyline(buf) == 1; ++line)
  557.         ;
  558.     if (fret != NULL && strncmp (buf,"options",7) == 0)
  559.     {
  560.         Options[0] = str_store(buf);
  561.         Optlines = 1;
  562.         trail = buf [strlen(buf)-2];
  563.         for ( ; (fret = fgets(buf,RECLEN-1,fp)) != NULL; ++line)
  564.         {
  565.             if (trail != '\\' && buf[0] != ' ' && buf[0] != '\t')
  566.                 break;
  567.             if (Optlines >= OPTLINES)
  568.                 printex ("%s - too many option lines (%d allowed)",Newsrc,OPTLINES);
  569.             Options[Optlines] = str_store(buf);
  570.             ++Optlines;
  571.             if ((len = strlen(buf)) >= 2 && buf[len-2] != '\\')
  572.                 trail = buf[len-2];
  573.             else
  574.                 trail = '\0';
  575.         }
  576.     }
  577.  
  578.     /* do the options from the newsrc file if there weren't command line args */
  579.     if (Optlines > 0 && optpflag == 'n')
  580.         newsrc_opt (lfirst,nun);
  581.  
  582.     for ( ; fret != NULL; ++line, fret = fgets(buf,RECLEN-1,fp))
  583.     {
  584.         if (emptyline(buf) == 1)
  585.             continue;
  586.         if ((ptr = strpbrk(buf,marks)) == NULL)
  587.         {
  588.             fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n",
  589.             line,Newsrc,buf);
  590.             continue;
  591.         }
  592.         submark = *ptr;
  593.         *ptr = '\0';
  594.         ++ptr;
  595.         num = 0;
  596.         for (ptr = strtok(ptr," ,-\n"); ptr != NULL; ptr = strtok(NULL," ,-\n"))
  597.         {
  598.             len = atoi (ptr);
  599.             for ( ; *ptr >= '0' && *ptr <= '9'; ++ptr)
  600.                 ;
  601.             if (*ptr != '\0' || len < num)
  602.             {
  603.                 num = -1;
  604.                 fprintf (stderr,"\nwarning: line %d of %s (%s) - bad syntax\n",
  605.                 line,Newsrc,buf);
  606.                 break;
  607.             }
  608.             num = len;
  609.         }
  610.         if (num < 0)
  611.             continue;
  612.         chkgroup (buf,submark,num,0);
  613.     }
  614.     fclose (fp);
  615.  
  616.     /* now take care of groups not specified in .newsrc */
  617.     art_active();
  618.  
  619.     /* free up the option string storage */
  620.     for (num=0; num < Ntopt; ++num)
  621.         regfree (Topt[num]);
  622.     for (num=0; num < Nwopt; ++num)
  623.         regfree (Wopt[num]);
  624.     for (num=0; num < Nntopt; ++num)
  625.         regfree (Negtopt[num]);
  626.     for (num=0; num < Nnwopt; ++num)
  627.         regfree (Negwopt[num]);
  628.     Ntopt = Nwopt = Nntopt = Nnwopt = 0;
  629.  
  630.     /* free the active list */
  631.     free ((char *) Active);
  632. }
  633.  
  634. static
  635. emptyline(s)
  636. char *s;
  637. {
  638.     while (isspace(*s))
  639.         ++s;
  640.     if (*s == '\0')
  641.         return (1);
  642.     return (0);
  643. }
  644.  
  645. /*
  646.     fill hash table from active news group list
  647.     This is needed to be able to process options
  648.     before scanning user order.  Constructs an array
  649.     of active newsgroup names for the rest of vns_nws().
  650. */
  651. static
  652. fill_active ()
  653. {
  654.     FILE *f;
  655.     char *nread, act_rec[RECLEN];
  656.     int num,lownum,rcount;
  657.  
  658.     Max_name = 0;
  659.     if ((f = fopen (ACTFILE,"r")) == NULL)
  660.         printex ("couldn't open %s\n",ACTFILE);
  661.  
  662.     /*
  663.     ** we do things this way so that we only examine active records
  664.     ** once, minimizing the window where changes could screw us up
  665.     ** at the cost of possibly alloc'ing a few extra bytes.  We start
  666.     ** with a count of one to have a positive rcount for alloc.
  667.     */
  668.     for(rcount=1; fgets(act_rec, RECLEN-1, f) != NULL; ++rcount)
  669.         ;
  670.     if ((Active = (char **) malloc(rcount*sizeof(char *))) == NULL)
  671.         printex("Memory allocation failure");
  672.  
  673.     rewind(f);
  674.  
  675.     Actnum = 0;
  676.     while (Actnum < rcount && fgets(act_rec, RECLEN-1, f) != NULL)
  677.     {
  678.         if (strtok (act_rec," \n") == NULL)
  679.             continue;
  680.         nread = strtok (NULL, " \n");
  681.         if (nread != NULL)
  682.             num = atoi(nread);
  683.         else
  684.             num = 0;
  685.         nread = strtok (NULL, " \n");
  686.         if (nread != NULL)
  687.             lownum = atoi(nread);
  688.         else
  689.             lownum = 0;
  690.         if (lownum > 0)
  691.             --lownum;
  692.         if (strlen(act_rec) > Max_name)
  693.             Max_name = strlen(act_rec);
  694.  
  695.         /* enter newsgroup, point to permanent copy of name */
  696.         hashenter (act_rec, num, lownum);
  697.         Active[Actnum] = (myfind(act_rec))->nd_name;
  698.         ++Actnum;
  699.     }
  700.  
  701.     fclose (f);
  702. }
  703.  
  704. /*
  705.     check active newsgroups not mentioned in NEWSRC file
  706.     (SFLG_SCAN not set)
  707. */
  708. static
  709. art_active ()
  710. {
  711.     int i;
  712.     NODE *ptr;
  713.  
  714.     for( i=0; i < Actnum; ++i)
  715.     {
  716.         ptr = myfind(Active[i]);
  717.         if ((ptr->state & SFLG_SCAN) == 0)
  718.             chkgroup (ptr->nd_name, NEWS_ON, 0, 1);
  719.     }
  720. }
  721.  
  722. /*
  723.     check group for new articles:
  724.     s - group
  725.     c - subscription indicator from NEWSRC
  726.     n - number read
  727.     new - new newsgroup flag
  728. */
  729. static
  730. chkgroup (s,c,n,new)
  731. char *s,c;
  732. int n;
  733. int new;
  734. {
  735.     NODE *ptr;
  736.     char sub;
  737.     int nrd;
  738.     int lowart;
  739.     int st;
  740.  
  741.     if ((ptr = hashfind(s)) != NULL && (ptr->state & SFLG_SCAN) == 0)
  742.     {
  743.         ptr->state |= SFLG_SCAN;
  744.  
  745. #ifdef SYN_CHECK
  746.         /* if "read" more than exist, reset */
  747.         if (n > ptr->highnum)
  748.         {
  749.             n = ptr->highnum - SYN_SETBACK;
  750.             fgprintf("%s: .newsrc out of synch, resetting\n",s);
  751.         }
  752. #endif
  753.         lowart = ptr->lownum;
  754.         if (n < ptr->lownum)
  755.             n = ptr->lownum;
  756.  
  757.         nrd = n;
  758.         sub = c;
  759.  
  760.         /*
  761.         ** scan decision is rather complex, since GF_ALL setting
  762.         ** overides "n" value, GF_SPEC indicates SFLG_SPEC flag used.
  763.         ** if GF_OVER set, SFLG_SPEC overides subscription mark, else
  764.         ** SFLG_SPEC AND subscribed is neccesary.
  765.         */
  766.         if ((Gflags & GF_SPEC) != 0)
  767.         {
  768.             if ((ptr->state & SFLG_SPEC) == 0)
  769.                 c = NEWS_OFF;
  770.             else
  771.             {
  772.                 if ((Gflags & GF_OVER) != 0)
  773.                     c = NEWS_ON;
  774.             }
  775.         }
  776.         if ((Gflags & GF_ALL) != 0)
  777.             n = lowart;
  778.         fw_group(s, new, sub == NEWS_ON, nrd, c == NEWS_ON);
  779.         if (c == NEWS_ON && ptr->highnum > n)
  780.         {
  781.             st = outgroup (s,n,ptr->highnum);
  782.             if (st > nrd)
  783.                 fw_chg(new, sub == NEWS_ON, st, c == NEWS_ON);
  784.         }
  785.     }
  786. }
  787.  
  788. /*
  789.     vns_write writes the .newsrc file
  790. */
  791. vns_write(news,ncount)
  792. NODE **news;
  793. int ncount;
  794. {
  795.     FILE *fp;
  796.     NODE *p;
  797.     char c;
  798.     int i,rc;
  799.  
  800.     if (link(Newsrc,Onews) < 0)
  801.         printex ("can't backup %s to %s before writing",Newsrc,Onews);
  802.  
  803.     if (unlink(Newsrc) < 0 || (fp = fopen(Newsrc,"w")) == NULL)
  804.         printex ("can't open %s for writing (backed up in %s)",Newsrc,Onews);
  805.     else
  806.     {
  807.         clearerr(fp);
  808.         for (i=0; (rc = ferror(fp)) == 0 && i < Optlines; ++i)
  809.             fprintf (fp,"%s",Options[i]);
  810.         for (i=0; rc == 0 && i < ncount; ++i)
  811.         {
  812.             p = news[i];
  813.             if ((p->flags & FLG_SUB) == 0)
  814.                 c = NEWS_OFF;
  815.             else
  816.                 c = NEWS_ON;
  817. #ifdef OLDRC
  818.             fprintf (fp,"%s%c %d\n",p->nd_name,c,p->rdnum);
  819. #else
  820.             if (p->rdnum > 0)
  821.                 fprintf(fp,"%s%c 1-%d\n",p->nd_name,c,p->rdnum);
  822.             else
  823.                 fprintf(fp,"%s%c 0\n",p->nd_name,c);
  824. #endif
  825.             rc = ferror(fp);
  826.         }
  827.         fclose (fp);
  828.         if (rc != 0)
  829.             printex ("write of %s failed, old copy stored in %s",Newsrc,Onews);
  830.         else
  831.             unlink (Onews);
  832.     }
  833. }
  834.  
  835. /*
  836.     arg_opt must be called prior to option scanning, since
  837.     it uses the options array.  This is a bit of a kludge,
  838.     but it saves a bunch of work.  NOTE - no command name argument
  839. */
  840. static
  841. arg_opt (argc,argv,lfirst,nun)
  842. int argc;
  843. char **argv;
  844. int *lfirst, *nun;
  845. {
  846.     if (argc > OPTLINES)
  847.         printex ("too many command line options (%d allowed)\n",OPTLINES);
  848.     for (Optlines=0; Optlines < argc; ++Optlines)
  849.     {
  850.         Options[Optlines] = *argv;
  851.         ++argv;
  852.     }
  853.     newsrc_opt(lfirst,nun);
  854. }
  855.  
  856. /*
  857.     option setting routine:
  858.     sets global flags: GF_ALL for -x option GF_SPEC for -n.
  859.     sets up filter array for article scanning
  860. */
  861. static
  862. newsrc_opt(lfirst,nun)
  863. int *lfirst, *nun;
  864. {
  865.     int i;
  866.     char curopt,tmp[RECLEN],*tok;
  867.  
  868.     *nun = *lfirst = 0;
  869.     Ntopt = Nwopt = Nnwopt = Nntopt = 0;
  870.     curopt = '\0';
  871.     for (i=0; i < Optlines; ++i)
  872.     {
  873.         strcpy(tmp,Options[i]);
  874.         for (tok = strtok(tmp,",\\ \t\n"); tok != NULL; tok = strtok(NULL,",\\ \t\n"))
  875.         {
  876.             if (*tok != '-')
  877.                 do_opt (curopt,tok);
  878.             else
  879.             {
  880.                 for (++tok; index("nwt",*tok) == NULL; ++tok)
  881.                 {
  882.                     /* options with no strings */
  883.                     switch(*tok)
  884.                     {
  885.                     case 'S':
  886.                         Gflags &= ~GF_OVER;
  887.                         break;
  888.                     case '%':
  889.                         *lfirst = 1;
  890.                         break;
  891.                     case 'U':
  892.                         *nun = 1;
  893.                         break;
  894. #ifdef OLDRC
  895.                     case 'i':
  896.                     /* Treat "-i" as synonym for "-x" */
  897. #endif
  898.                     case 'x':
  899.                         Gflags |= GF_ALL;
  900.                     default:
  901.                         break;
  902.                     }
  903.                 }
  904.                 curopt = *tok;
  905.                 if (*(++tok) != '\0')
  906.                     do_opt (curopt,tok);
  907.             }
  908.         }
  909.     }
  910. }
  911.  
  912. /* do_opt is for options with strings attached */
  913. static
  914. do_opt (opt,str)
  915. char opt, *str;
  916. {
  917.     switch (opt)
  918.     {
  919.     case 'n':
  920.         Gflags |= GF_SPEC;
  921.         specmark(str);
  922.         break;
  923.     case 'w':
  924.         specfilter (FIL_AUTHOR,str);
  925.         break;
  926.     case 't':
  927.         specfilter (FIL_TITLE,str);
  928.         break;
  929.     default:
  930. #ifdef OLDRC
  931.         Gflags |= GF_SPEC;    /* Assume anything else is newsgroup */
  932.         specmark(str);
  933. #endif
  934.         break;
  935.     }
  936. }
  937.  
  938. static
  939. specfilter (comp,str)
  940. char comp,*str;
  941. {
  942.     int *count;
  943.     char **rex;
  944.  
  945.     /*
  946.     ** we may set rex one past end of array.  we will error before
  947.     ** referencing it if that's the case, however.
  948.     */
  949.     if (*str == '!')
  950.     {
  951.         if (comp == FIL_TITLE)
  952.         {
  953.             count = &Nntopt;
  954.             rex = Negtopt + *count;
  955.         }
  956.         else
  957.         {
  958.             count = &Nnwopt;
  959.             rex = Negwopt + *count;
  960.         }
  961.         ++str;
  962.     }
  963.     else
  964.     {
  965.         if (comp == FIL_TITLE)
  966.         {
  967.             count = &Ntopt;
  968.             rex = Topt + *count;
  969.         }
  970.         else
  971.         {
  972.             count = &Nwopt;
  973.             rex = Wopt + *count;
  974.         }
  975.     }
  976.     if (*count >= NUMFILTER)
  977.         printex ("too many %c options, %d allowed",comp,NUMFILTER);
  978.     if ((*rex = regcmp(str,(char *) 0)) == NULL)
  979.         printex ("%c option regular expression syntax: %s",comp,str);
  980.     ++(*count);
  981. }
  982.  
  983. /*
  984.     handle the newsgroup specification string.
  985.     ("all" convention - braack!!!)
  986. */
  987. static
  988. specmark (s)
  989. char *s;
  990. {
  991.     unsigned ormask,andmask;
  992.     int i,len;
  993.     char *ptr,*re,pattern[RECLEN];
  994.     NODE *nptr;
  995.  
  996.     if (*s == '!')
  997.     {
  998.         ++s;
  999.         ormask = 0;
  1000.         andmask = ~SFLG_SPEC;
  1001.         if (*s == '\0')
  1002.             return;
  1003.     }
  1004.     else
  1005.     {
  1006.         ormask = SFLG_SPEC;
  1007.         andmask = 0xffff;
  1008.     }
  1009.  
  1010.     /* convert "all" not bounded by alphanumerics to ".*". ".all" becomes ".*" */
  1011.     for (ptr = s; (len = findall(ptr)) >= 0; ptr += len+1)
  1012.     {
  1013.         if (len > 0 && isalnum (s[len-1]))
  1014.             continue;
  1015.         if (isalnum (s[len+3]))
  1016.             continue;
  1017.         if (len > 0 && s[len-1] == '.')
  1018.         {
  1019.             --len;
  1020.             strcpy (s+len,s+len+1);
  1021.         }
  1022.         s[len] = '.';
  1023.         s[len+1] = '*';
  1024.         strcpy (s+len+2,s+len+3);
  1025.     }
  1026.  
  1027.     /* now use regular expressions */
  1028.     sprintf (pattern,"^%s$",s);
  1029.     if ((re = regcmp(pattern,(char *) 0)) == NULL)
  1030.         printex ("n option regular expression syntax: %s",s);
  1031.     for (i=0; i < Actnum; ++i)
  1032.     {
  1033.         nptr = myfind(Active[i]);
  1034.         if (regex(re,nptr->nd_name) != NULL)
  1035.         {
  1036.             nptr->state |= ormask;
  1037.             nptr->state &= andmask;
  1038.         }
  1039.     }
  1040.     regfree (re);
  1041. }
  1042.  
  1043. static
  1044. findall (s)
  1045. char *s;
  1046. {
  1047.     int len;
  1048.     for (len=0; *s != '\0'; ++s,++len)
  1049.     {
  1050.         if (*s == 'a' && strncmp(s,"all",3) == 0)
  1051.             return (len);
  1052.     }
  1053.     return (-1);
  1054. }
  1055.  
  1056. static
  1057. grp_indic (s,ok)
  1058. char *s;
  1059. int ok;
  1060. {
  1061.     if (ok)
  1062.         fgprintf("    %s\n",s);
  1063.     else
  1064.         fgprintf("    %s - Can't access spool directory\n",s);
  1065. }
  1066.  
  1067. /*
  1068.     enter newsgroup articles.
  1069.     all articles between low and hi are to be included.
  1070.  
  1071.     Returns the highest number less than an OPENED (not neccesarily
  1072.     accepted) article to allow caller to revise "articles read"
  1073.     number beyond non-existent articles.
  1074. */
  1075. outgroup (s,low,hi)
  1076. char *s;
  1077. int low,hi;
  1078. {
  1079.     int i;
  1080.     char subj[RECLEN], lines[RECLEN], auth[RECLEN], gd[RECLEN];
  1081.     int ret,op;
  1082.  
  1083.     if ((hi-low) > MAXARTRANGE)
  1084.         low = hi - MAXARTRANGE;
  1085.  
  1086.     ret = low;
  1087.     op = 1;
  1088.  
  1089.     g_dir(s,gd);
  1090.     if (chdir(gd) < 0)
  1091.     {
  1092.         grp_indic(s,0);
  1093.         return (ret);
  1094.     }
  1095.     grp_indic(s,1);
  1096.     for (i=low+1; i <= hi; ++i)
  1097.     {
  1098.         if (digname(i,subj,lines,auth,&op) >= 0)
  1099.         {
  1100.             fw_art(i,subj,lines,auth);
  1101.         }
  1102.         else
  1103.         {
  1104.             if (op)
  1105.                 ret = i;
  1106.         }
  1107.     }
  1108.  
  1109.     return(ret);
  1110. }
  1111.  
  1112. /*
  1113. ** open article and interpret options, if any.  The op parameter is set
  1114. ** to ZERO if and only if an article is opened.  Used above as a flag to
  1115. ** indicate no articles opened yet.
  1116. */
  1117. static digname (n, subj, lines, auth, op)
  1118. int n;
  1119. char *subj, *lines, *auth;
  1120. int *op;
  1121. {
  1122.     int i,j;
  1123.     FILE *fp;
  1124.     char t[RECLEN];
  1125.     char *nfgets();
  1126.  
  1127.     /* open article */
  1128.     sprintf (t,"%d", n);
  1129.     if ((fp = fopen(t,"r")) == NULL)
  1130.         return (-1);
  1131.     *op = 0;
  1132.  
  1133.     /* get subject, from and lines by reading article */
  1134.     subj[0] = lines[0] = auth[0] = '?';
  1135.     subj[1] = lines[1] = auth[1] = '\0';
  1136.     for (i = 0; i < HDR_LINES && nfgets(t,RECLEN-1,fp) != NULL; ++i)
  1137.     {
  1138.         if (index(CHFIRST,t[0]) == NULL)
  1139.             continue;
  1140.         t[strlen(t) - 1] = '\0';
  1141.         if (strncmp(T_head,t,THDLEN) == 0)
  1142.         {
  1143.             for (j=0; j < Nntopt; ++j)
  1144.             {
  1145.                 if (regex(Negtopt[j],t+THDLEN) != NULL)
  1146.                 {
  1147.                     fclose(fp);
  1148.                     return(-1);
  1149.                 }
  1150.             }
  1151.             if (Ntopt > 0)
  1152.             {
  1153.                 for (j=0; j < Ntopt; ++j)
  1154.                 {
  1155.                     if (regex(Topt[j],t+THDLEN) != NULL)
  1156.                         break;
  1157.                 }
  1158.                 if (j >= Ntopt)
  1159.                 {
  1160.                     fclose(fp);
  1161.                     return(-1);
  1162.                 }
  1163.             }
  1164.             strcpy(subj,t+THDLEN);
  1165.             continue;
  1166.         }
  1167.         if (strncmp(F_head,t,FHDLEN) == 0)
  1168.         {
  1169.             for (j=0; j < Nnwopt; ++j)
  1170.             {
  1171.                 if (regex(Negwopt[j],t+FHDLEN) != NULL)
  1172.                 {
  1173.                     fclose(fp);
  1174.                     return(-1);
  1175.                 }
  1176.             }
  1177.             if (Nwopt > 0)
  1178.             {
  1179.                 for (j=0; j < Nwopt; ++j)
  1180.                 {
  1181.                     if (regex(Wopt[j],t+FHDLEN) != NULL)
  1182.                         break;
  1183.                 }
  1184.                 if (j >= Nwopt)
  1185.                 {
  1186.                     fclose(fp);
  1187.                     return(-1);
  1188.                 }
  1189.             }
  1190.             strcpy(auth,t+FHDLEN);
  1191.             continue;
  1192.         }
  1193.         if (strncmp(L_head,t,LHDLEN) == 0)
  1194.         {
  1195.             strcpy(lines,t+LHDLEN);
  1196.             break;
  1197.         }
  1198.     }
  1199.  
  1200.     fclose (fp);
  1201.  
  1202.     /* reject empty or 1 line files */
  1203.     if (i < 2)
  1204.         return (-1);
  1205.  
  1206.     return (0);
  1207. }
  1208.  
  1209. /*
  1210. ** special fgets for reading header lines, which unfolds continued lines
  1211. ** and throws away trailing stuff on buffer overflow.
  1212. */
  1213. static char *
  1214. nfgets(buf, size, fp)
  1215. char    *buf;
  1216. int    size;
  1217. FILE    *fp;
  1218. {
  1219.     register int c;
  1220.  
  1221.     while (!feof(fp))
  1222.     {
  1223.         if ((c = getc(fp)) == '\n')
  1224.         {
  1225.             if ((c = getc(fp)) == '\t' || c == ' ')
  1226.                 continue;
  1227.             ungetc(c, fp);
  1228.             *buf = '\n';
  1229.             ++buf;
  1230.             *buf = '\0';
  1231.             ++buf;
  1232.             return (buf);
  1233.         }
  1234.  
  1235.         /* prevent "terminal bombs" */
  1236.         if (c < ' ' || c == '\177')
  1237.         {
  1238.             switch(c)
  1239.             {
  1240.             case '\r':
  1241.             case '\010':
  1242.             case '\07':
  1243.                 break;
  1244.             case '\177':
  1245.                 c = '~';
  1246.                 break;
  1247.             case '\t':
  1248.                 c = ' ';
  1249.                 break;
  1250.             default:
  1251.                 if (size > 1)
  1252.                 {
  1253.                     *buf = '^';
  1254.                     ++buf;
  1255.                     --size;
  1256.                 }
  1257.                 c += 'A' - 1;
  1258.                 break;
  1259.             }
  1260.         }
  1261.  
  1262.         if (size > 0)
  1263.         {
  1264.             *buf = c;
  1265.             ++buf;
  1266.             --size;
  1267.         }
  1268.         if (c == '\r')
  1269.         {
  1270.             if ((c = getc(fp)) != '\n')
  1271.             {
  1272.                 ungetc(c, fp);
  1273.                 continue;
  1274.             }
  1275.             if ((c = getc(fp)) != ' ' && c != '\t')
  1276.             {
  1277.                 *buf = '\0';
  1278.                 ++buf;
  1279.                 ungetc(c, fp);
  1280.                 return (buf);
  1281.             }
  1282.             --buf;
  1283.             ++size;
  1284.             continue;
  1285.         }
  1286.     }
  1287.  
  1288.     *buf = '\0';
  1289.     ++buf;
  1290.     return (NULL);
  1291. }
  1292.  
  1293. static char *Mail[2], *Show[6], *Post[4];
  1294. static char *Priv[8];
  1295. static char *Pool = NULL;
  1296.  
  1297. FILE *
  1298. vns_aopen(art,hdr)
  1299. int art;
  1300. ARTHEADER *hdr;
  1301. {
  1302.     char buf[RECLEN];
  1303.     char *dist, *reply, *from, *ngrp, *flto, *path, *resubj;
  1304.     FILE *fp;
  1305.     int n;
  1306.     char *mail_trim();
  1307.  
  1308.     dist = resubj = path = reply = from = ngrp = flto = NULL;
  1309.  
  1310.     sprintf(buf,"%d",art);
  1311.     if ((fp = fopen(buf,"r")) == NULL)
  1312.         return(NULL);
  1313.  
  1314.     /*
  1315.     ** we only really need a lot extra if MAILCHOOSE, but allocating
  1316.     ** a temporary array of pointers isn't that much.  Similarly, a
  1317.     ** few assignments, and the "Priv" declaration are only needed
  1318.     ** with some define settings.  Not worth ifdef'ing.
  1319.     */
  1320.     Pool = str_tpool(100);
  1321.  
  1322.     hdr->artid = "<some article>";
  1323.     hdr->from = "<somebody>";
  1324.     hdr->priv = Priv;
  1325.     hdr->postcmd = Poster;
  1326.     hdr->mail = Mail;
  1327.     hdr->show = Show;
  1328.     hdr->post = Post;
  1329.     hdr->priv_num = hdr->show_num = hdr->post_num = hdr->mail_num = 0;
  1330.  
  1331.     /* for conditional is abnormal - expected exit is break */
  1332.     for (n=0; n < HDR_LINES && fgets(buf,RECLEN-1,fp) != NULL; ++n)
  1333.     {
  1334.         /* bail out at first non-header line */
  1335.         if (buf[0] == '\n')
  1336.             break;
  1337.         if (strncmp(buf,RT_head,RTHDLEN) == 0)
  1338.         {
  1339.             buf [strlen(buf)-1] = '\0';
  1340.             reply = str_tstore(Pool,buf+RTHDLEN);
  1341.             continue;
  1342.         }
  1343.         if (strncmp(buf,P_head,PHDLEN) == 0)
  1344.         {
  1345.             buf [strlen(buf)-1] = '\0';
  1346.             path = str_tstore(Pool,buf+PHDLEN);
  1347.             continue;
  1348.         }
  1349.         if (strncmp(buf,DIS_head,DISHDLEN) == 0)
  1350.         {
  1351.             buf [strlen(buf)-1] = '\0';
  1352.             dist = str_tstore(Pool,buf);
  1353.             continue;
  1354.         }
  1355.         if (strncmp(buf,M_head,MHDLEN) == 0)
  1356.         {
  1357.             buf [strlen(buf)-1] = '\0';
  1358.             hdr->artid = str_tstore(Pool,buf+MHDLEN);
  1359.             continue;
  1360.         }
  1361.         if (strncmp(buf,F_head,FHDLEN) == 0)
  1362.         {
  1363.             buf [strlen(buf)-1] = '\0';
  1364.             (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
  1365.             from = hdr->from = (hdr->show)[hdr->show_num]+FHDLEN;
  1366.             ++(hdr->show_num);
  1367.             continue;
  1368.         }
  1369.         if (strncmp(buf,T_head,THDLEN) == 0)
  1370.         {
  1371.             buf [strlen(buf)-1] = '\0';
  1372.             (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
  1373.             if (strncmp(buf+THDLEN,Fpfix,FPFLEN) != 0)
  1374.             {
  1375.                 sprintf(buf,"%s%s%s",T_head,Fpfix,
  1376.                     ((hdr->show)[hdr->show_num])+THDLEN);
  1377.                 resubj = str_tstore(Pool,buf);
  1378.             }
  1379.             else
  1380.                 resubj = (hdr->show)[hdr->show_num];
  1381.             ++(hdr->show_num);
  1382.             continue;
  1383.         }
  1384.         if (strncmp(buf,N_head,NHDLEN) == 0)
  1385.         {
  1386.             buf [strlen(buf)-1] = '\0';
  1387.  
  1388.             /* if multiple newsgroups, include in "show" */
  1389.             if (index(buf,',') != NULL)
  1390.             {
  1391.                 (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
  1392.                 ngrp = (hdr->show)[hdr->show_num] + NHDLEN;
  1393.                 ++(hdr->show_num);
  1394.             }
  1395.             else
  1396.                 ngrp = str_tstore(Pool,buf+NHDLEN);
  1397.             continue;
  1398.         }
  1399.         if (strncmp(buf,FT_head,FTHDLEN) == 0)
  1400.         {
  1401.             buf [strlen(buf)-1] = '\0';
  1402.             (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
  1403.             flto = (hdr->show)[hdr->show_num] + FTHDLEN;
  1404.             ++(hdr->show_num);
  1405.             continue;
  1406.         }
  1407.         if (strncmp(buf,L_head,LHDLEN) == 0)
  1408.         {
  1409.             buf [strlen(buf)-1] = '\0';
  1410.             hdr->lines = atoi(buf+LHDLEN);
  1411.             (hdr->show)[hdr->show_num] = str_tstore(Pool,buf);
  1412.             ++(hdr->show_num);
  1413.             continue;
  1414.         }
  1415.     }
  1416.  
  1417.     hdr->hlines = n;
  1418.  
  1419. #ifdef MAILCHOOSE
  1420.     (hdr->priv)[hdr->priv_num] = resubj;
  1421.     ++(hdr->priv_num);
  1422.     if (reply != NULL)
  1423.     {
  1424.         (hdr->priv)[hdr->priv_num] = mail_trim(reply);
  1425.         ++(hdr->priv_num);
  1426.     }
  1427.     if (from != NULL)
  1428.     {
  1429.         (hdr->priv)[hdr->priv_num] = mail_trim(from);
  1430.         ++(hdr->priv_num);
  1431.     }
  1432.     if (path != NULL)
  1433.     {
  1434.         (hdr->priv)[hdr->priv_num] = mail_trim(path);
  1435.         ++(hdr->priv_num);
  1436.     }
  1437. #else
  1438. #ifdef MAILSMART
  1439.     if (reply == NULL)
  1440.         if (from != NULL)
  1441.             reply = from;
  1442.         else
  1443.         {
  1444.             if (path != NULL)
  1445.                 reply = path;
  1446.         }
  1447. #else
  1448.     if (path != NULL)
  1449.         reply = path;
  1450. #endif
  1451.     if (reply != NULL)
  1452.         reply =  mail_trim(reply);
  1453.     mail_cmd(hdr,reply,resubj);
  1454. #endif /* MAILCHOOSE */
  1455.  
  1456.     if (flto == NULL)
  1457.     {
  1458.         if ((flto = ngrp) == NULL)
  1459.             flto = "group.unknown";
  1460.     }
  1461.     ngrp = rindex(flto,'.');
  1462.  
  1463.     if (strncmp("mod.",flto,4) == 0 ||
  1464.             (ngrp != NULL && strcmp(".announce",ngrp) == 0))
  1465.     {
  1466.         sprintf(buf,"Cannot post a follow-up to \"%s\", reply with mail to moderator",flto);
  1467.         hdr->post_err = str_tstore(Pool,buf);
  1468.         return (fp);
  1469.     }
  1470.  
  1471.     if (ngrp != NULL && strcmp(ngrp,".general") == 0)
  1472.     {
  1473.         *ngrp = '\0';
  1474.         sprintf(buf,"%s%s.followup",N_head,flto);
  1475.     }
  1476.     else
  1477.         sprintf(buf,"%s%s",N_head,flto);
  1478.     flto = str_tstore(Pool,buf);
  1479.  
  1480.     hdr->post_err = NULL;
  1481.  
  1482.     if (resubj != NULL)
  1483.     {
  1484.         (hdr->post)[hdr->post_num] = resubj;
  1485.         ++(hdr->post_num);
  1486.     }
  1487.  
  1488.     (hdr->post)[hdr->post_num] = flto;
  1489.     ++(hdr->post_num);
  1490.  
  1491.     sprintf(buf,"%s%s",R_head,hdr->artid);
  1492.     (hdr->post)[hdr->post_num] = str_tstore(Pool,buf);
  1493.     ++(hdr->post_num);
  1494.  
  1495.     if (dist != NULL)
  1496.     {
  1497.         (hdr->post)[hdr->post_num] = dist;
  1498.         ++(hdr->post_num);
  1499.     }
  1500.  
  1501.     return (fp);
  1502. }
  1503.  
  1504. #ifdef MAILCHOOSE
  1505. /*
  1506. ** routine to prompt user for mail path approval
  1507. */
  1508. static
  1509. mail_prompt(hdr)
  1510. ARTHEADER *hdr;
  1511. {
  1512.     int i;
  1513.     char buf[RECLEN],*ptr;
  1514.  
  1515.     tty_set(SAVEMODE);
  1516.     for (i=1; i < hdr->priv_num; ++i)
  1517.     {
  1518.         printf("%d - %s\n",i,(hdr->priv)[i]);
  1519.     }
  1520.     printf("\nType number to choose one of the above, or input address: ");
  1521.     fgets(buf,RECLEN-1,stdin);
  1522.     tty_set(RESTORE);
  1523.  
  1524.     ptr = strtok(buf," \t\n");
  1525.     if (ptr == NULL)
  1526.         ptr = "";
  1527.  
  1528.     i = strlen(ptr);
  1529.     if (i == 1)
  1530.     {
  1531.         i = atoi(ptr);
  1532.         if (i > 0 && i <= hdr->priv_num)
  1533.             ptr = (hdr->priv)[i];
  1534.         i = 1;
  1535.     }
  1536.  
  1537.     /*
  1538.     ** If the user keeps cycling through here on the same article,
  1539.     ** we will eventually run out of strings.  We made Pool large
  1540.     ** enough to make it unlikely (user will have to retry about 80
  1541.     ** times without switching articles).  Hardly elegant, but should
  1542.     ** be sufficient.
  1543.     */
  1544.     if (i > 1 && hdr->priv_num < 8)
  1545.     {
  1546.         (hdr->priv)[hdr->priv_num] = str_tstore(Pool,ptr);
  1547.         ++(hdr->priv_num);
  1548.     }
  1549.     mail_cmd(hdr,ptr,(hdr->priv)[0]);
  1550. }
  1551. #endif
  1552.  
  1553. /*
  1554. ** trim () off potential mail address, and make copy if needed.
  1555. ** addr must be allocated string.
  1556. */
  1557. static char *
  1558. mail_trim(addr)
  1559. char *addr;
  1560. {
  1561.     char buf[RECLEN];
  1562.     char *ptr;
  1563.  
  1564.     if (index(addr,'(') == NULL)
  1565.         return(addr);
  1566.  
  1567.     strcpy(buf,addr);
  1568.     ptr = index(buf,'(');
  1569.     for (--ptr; *ptr == ' ' || *ptr == '\t'; --ptr)
  1570.         ;
  1571.     ++ptr;
  1572.     *ptr = '\0';
  1573.     return (str_tstore(Pool,buf));
  1574. }
  1575.  
  1576. /*
  1577. ** format mail command.  Subj must point to allocated string.
  1578. */
  1579. static
  1580. mail_cmd(hdr,addr,subj)
  1581. ARTHEADER *hdr;
  1582. char *addr, *subj;
  1583. {
  1584.     char buf[RECLEN];
  1585.  
  1586.     if (addr == NULL || *addr == '\0')
  1587.     {
  1588.         hdr->mail_err = "No address";
  1589.         return;
  1590.     }
  1591.  
  1592.     hdr->mail_err = NULL;
  1593.             ;
  1594.  
  1595. #ifdef INLETTER
  1596.     hdr->mailcmd = Mailer;
  1597.     sprintf(buf,"%s%s",TO_head,addr);
  1598.     (hdr->mail)[0] = str_tstore(Pool,buf);
  1599.     hdr->mail_num = 1;
  1600. #else
  1601.     sprintf(buf,Mailer,addr);
  1602.     hdr->mailcmd = str_tstore(Pool,buf);
  1603.     hdr->mail_num = 0;
  1604. #endif
  1605.     if (subj != NULL)
  1606.     {
  1607.         (hdr->mail)[hdr->mail_num] = subj;
  1608.         ++(hdr->mail_num);
  1609.     }
  1610. }
  1611.  
  1612. vns_aclose(fp)
  1613. FILE *fp;
  1614. {
  1615.      if (Pool != NULL)
  1616.          str_tfree(Pool);
  1617.      Pool = NULL;
  1618.     fclose(fp);
  1619. }
  1620.  
  1621. /*
  1622. ** we don't use the count / name / mode arguments because this doesn't
  1623. ** implement any fancy article massaging
  1624. */
  1625. vns_asave(art,fp)
  1626. int art;
  1627. FILE *fp;
  1628. {
  1629.     char buf[RECLEN];
  1630.     FILE *fin;
  1631.  
  1632.     sprintf(buf,"%d",art);
  1633.     if ((fin = fopen(buf,"r")) == NULL)
  1634.         return;
  1635.  
  1636.     while (fgets(buf,RECLEN-1,fin) != NULL)
  1637.         fputs(buf,fp);
  1638.     fclose(fin);
  1639. }
  1640.  
  1641. vns_exit()
  1642. {
  1643. }
  1644. SHAR_EOF
  1645. fi # end of overwriting check
  1646. echo shar: extracting "'std.h'" '(886 characters)'
  1647. if test -f 'std.h'
  1648. then
  1649.     echo shar: will not over-write existing file "'std.h'"
  1650. else
  1651. cat << \SHAR_EOF > 'std.h'
  1652. /*
  1653.     newsrc states
  1654. */
  1655. #define NEWS_ON ':'
  1656. #define NEWS_OFF '!'
  1657.  
  1658. #define SFLG_SCAN 1
  1659. #define SFLG_SPEC 2
  1660.  
  1661. #define FPFIX "Re: "
  1662. #define FPFLEN 4
  1663.  
  1664. #define FIL_AUTHOR 'w'
  1665. #define FIL_TITLE 't'
  1666.  
  1667. /*
  1668.     header lines and associated lengths.  Strings should
  1669.     actually be used only once.
  1670. */ 
  1671. #define RHEAD "References: "
  1672. #define RHDLEN 12
  1673. #define MHEAD "Message-ID: "
  1674. #define MHDLEN 12
  1675. #define PHEAD "Path: "
  1676. #define PHDLEN 6
  1677. #define DHEAD "Date: "
  1678. #define DHDLEN 6
  1679. #define RTHEAD "Reply-To: "
  1680. #define RTHDLEN 10
  1681. #define TOHEAD "To: "
  1682. #define TOHDLEN 4
  1683. #define FHEAD "From: "
  1684. #define FHDLEN 6
  1685. #define FTHEAD "Followup-To: "
  1686. #define FTHDLEN 13
  1687. #define DISHEAD "Distribution: "
  1688. #define DISHDLEN 14
  1689. #define THEAD "Subject: "
  1690. #define THDLEN 9
  1691. #define LHEAD "Lines: "
  1692. #define LHDLEN 7
  1693. #define NHEAD "Newsgroups: "
  1694. #define NHDLEN 12
  1695.  
  1696. #define CHFIRST "FSL"    /* first char's of those used in page display */
  1697. SHAR_EOF
  1698. fi # end of overwriting check
  1699. echo shar: extracting "'config_std.h'" '(2824 characters)'
  1700. if test -f 'config_std.h'
  1701. then
  1702.     echo shar: will not over-write existing file "'config_std.h'"
  1703. else
  1704. cat << \SHAR_EOF > 'config_std.h'
  1705. /*
  1706. ** default news poster
  1707. */
  1708. #define DEF_POST "/usr/lib/news/inews -h"
  1709.  
  1710. /*
  1711. ** default user .newsrc file
  1712. */
  1713. #define DEF_NEWSRC ".newsrc"
  1714.  
  1715. /*
  1716. ** If INLETTER is defined, the address line will be placed into the
  1717. ** file editted by the user, and the mailer is assumed smart enough
  1718. ** to understand about header lines in the file.  Otherwise the
  1719. ** address is part of the mailer's command line.
  1720. **
  1721. ** if MAILSMART is defined, The From: line will be used for mail replies,
  1722. ** or overridden by a "Reply-to:" line if present - "Path:" will be used
  1723. ** as a last resort.  If MAILSMART is not defined, "Path:" will simply be
  1724. ** used.
  1725. **
  1726. ** if MAILCHOOSE is defined, the user is prompted before edit with all
  1727. ** of the address lines to choose from, or to input a new one.  MAILCHOOSE
  1728. ** makes MAILSMART irrelevant, but the two are independent of INLETTER.
  1729. **
  1730. #define MAILCHOOSE
  1731. */
  1732. #define MAILSMART
  1733. #define INLETTER
  1734.  
  1735. /*
  1736. ** default mail sender.  If INLETTER, will be done as
  1737. ** cat <file> | DEF_MAIL,  Otherwise, cat <file> | DEF_MAIL <address>
  1738. ** user's MAILER variable will have to conform, too.
  1739. */
  1740. #ifdef INLETTER
  1741. #define DEF_MAIL "/usr/lib/sendmail -t"
  1742. #else
  1743. #define DEF_MAIL "/bin/mail"
  1744. #endif
  1745. ** OLDRC defined for an apparently earlier news version which took unnamed
  1746. ** command line options as synonyms for -n, and did not take ranges in
  1747. ** the .newsrc file.  Probably useless, but kept in for historical reasons.
  1748. **
  1749. **#define OLDRC
  1750. */
  1751.  
  1752. /*
  1753. ** article spool directory
  1754. */
  1755. #define SPOOLDIR "/usr/spool/news"
  1756.  
  1757. /*
  1758. ** active file
  1759. */
  1760. #define ACTFILE "/usr/lib/news/active"
  1761.  
  1762. /*
  1763. ** maximum number of option lines in .newsrc
  1764. */
  1765. #define OPTLINES 60
  1766.  
  1767. /*
  1768. ** maximum number of filter options
  1769. */
  1770. #define NUMFILTER 30
  1771.  
  1772. /*
  1773. ** maximum number of file lines to search looking for header lines.
  1774. */
  1775. #define HDR_LINES 36
  1776.  
  1777. /*
  1778. ** When a newsgroup is scanned, we ignore articles less than <high spool> -
  1779. ** MAXARTRANGE.  This is intended to prevent ridiculous numbers of article
  1780. ** opening attempts the first time a user reads a new newsgroup which has a
  1781. ** huge difference between the high and low spool numbers, perhaps due to
  1782. ** some articles not getting expired.
  1783. */
  1784. #define MAXARTRANGE 1600    /* about 2 weeks of soc.singles */
  1785.  
  1786. /*
  1787. ** If we detect that the user has a higher number in .newsrc than the
  1788. ** high article number, obviously the active file is out of synch with the
  1789. ** .newsrc.  We set the user's number back to the low article number in
  1790. ** this case, on the theory that it's better to repeat stuff than miss
  1791. ** articles.  On such setbacks, we won't backdate the user by more than
  1792. ** SYN_SETBACK articles, preventing floods of articles on large newsgroups
  1793. ** if you don't define SYN_CHECK, the user's number won't be adjusted in
  1794. ** this case, choosing to lose articles rather than show old ones.
  1795. */
  1796. #define SYN_CHECK
  1797. #define SYN_SETBACK 60
  1798. SHAR_EOF
  1799. fi # end of overwriting check
  1800. #    End of shell archive
  1801. exit 0
  1802.